﻿using System.Linq;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace Babylon.Toolbox.ModelBuilding
{
    public class ModelContent
    {
        readonly List<MeshContent> _meshes = new List<MeshContent>();
        public List<MeshContent> Meshes
        {
            get
            {
                return _meshes;
            }
        }

        public Bone RootBone { get; set; }

        internal void AddChild(MeshContent meshContent)
        {
            _meshes.Add(meshContent);
        }

        public Model BuildModel(GraphicsDevice device, IMaterialConverter materialConverter)
        {
            BoundingSphere globalSphere = new BoundingSphere();
            var groups = Meshes.GroupBy(key => key.VertexDeclaration);
            int indexBufferSize = Meshes.Sum(mesh => mesh.FaceCount) * 3;
            var indexBuffer = new IndexBuffer(device, IndexElementSize.SixteenBits, indexBufferSize, BufferUsage.WriteOnly);

            List<VertexBuffer> staginVertexBuffers = new List<VertexBuffer>();
            List<ModelMesh> meshes = new List<ModelMesh>();
            foreach (var group in groups)
            {
                int count = group.Sum(key => key.VerticesCount);

                VertexBuffer buffer = new VertexBuffer(device, group.Key, count, BufferUsage.WriteOnly);
                staginVertexBuffers.Add(buffer);

                int vertexBufferID = staginVertexBuffers.Count - 1;
                int vertexBufferOffset = 0;
                int verticesCount = 0;
                int indexBufferOffset = 0;
                int indicesCount = 0;

                foreach (var modelMeshContent in group)
                {
                    var modelMesh = new ModelMesh
                                        {
                                            VertexBufferId = vertexBufferID, 
                                            VerticesOffset = verticesCount
                                        };
                    // Vertices
                    Vector3[] positions;
                    BoundingSphere boundingSphere;
                    vertexBufferOffset = modelMeshContent.AppendToVertexBuffer(buffer, vertexBufferOffset, out boundingSphere, out positions);
                    verticesCount += modelMeshContent.VerticesCount;

                    // Bounding sphere
                    modelMesh.BoundingSphere = boundingSphere;

                    globalSphere = BoundingSphere.CreateMerged(globalSphere, boundingSphere);

                    // Indices
                    int[] indices;
                    modelMesh.IndicesOffset = indicesCount;
                    
                    indexBufferOffset = modelMeshContent.AppendToIndexBuffer(indexBuffer, indexBufferOffset, out indices);
                    indicesCount += modelMeshContent.FaceCount * 3;

                    // Picking
                    if (modelMeshContent.GeneratePickingInformations)
                    {
                        modelMesh.SetPickingData(positions, indices);
                    }

                    // Parts
                    modelMeshContent.BuildMeshParts(modelMesh, device, materialConverter);
                    // ModelContent only supports 1 Index Buffer
                    modelMesh.IndexBufferId = 0;
                    meshes.Add(modelMesh);
                    modelMesh.VerticesCount = modelMeshContent.VerticesCount;
                }
            }
            var model = new Model(device, staginVertexBuffers, new List<IndexBuffer>{indexBuffer}, meshes)
                            {
                                BoundingSphere = globalSphere
                            };
            return model;
        }

    }

    public interface IMaterialConverter
    {
        ModelEffect Convert(GraphicsDevice device, MaterialContent material);
    }
}
